import time
import os
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib
import matplotlib.pyplot as plt
from typing import List, Union
import numpy as np
import random

from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter

matplotlib.use("Agg")


def randomcolor():
    colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
    color = "#" + ''.join([random.choice(colorArr) for i in range(6)])
    return color


class PlotRoute:
    def __init__(self, rate: Union[float, None] = None):
        if not isinstance(rate, float) or rate <= 0:
            self.rate = 0.1
        else:
            self.rate = rate

        self.file_path_prefix = "tmp/img/"

        if not os.path.isdir(self.file_path_prefix):
            os.makedirs(self.file_path_prefix)

    def make_plot(self, ship_info_list: List[tuple], docker_result: dict) -> str:
        """
        作图
        :param ship_info_list: 航行信息列表
        :param docker_result: 停靠港口的统计结果
        :return: 文件路径
        """
        lats = [info[1] for info in ship_info_list]
        lons = [info[2] for info in ship_info_list]

        port_lats = [info["lat"] for info in docker_result]
        port_lons = [info["lon"] for info in docker_result]
        port_names = [info["portName"] for info in docker_result]

        # 由于航线可能会经过180度经线,需要对经度做一个平移
        # 这里线求偏移量
        prev = left_bound = right_bound = lons[0]
        flag = 0
        for lon in lons:
            # 第一次跨越180度经线
            if not flag and abs(prev - lon) > 90:
                flag = 1
                if lon < 0:
                    right_bound = left_bound
                    left_bound = lon
                else:
                    left_bound = right_bound
                    right_bound = lon
            if not flag:
                left_bound = min(left_bound, lon)
                right_bound = max(right_bound, lon)
            else:
                if lon > (left_bound + right_bound) / 2:
                    right_bound = min(right_bound, lon)
                else:
                    left_bound = max(left_bound, lon)
            prev = lon

        if flag:
            # 最左的经度
            left_lon = (right_bound + left_bound) / 2
        else:
            left_lon = -180
        # 位于中心的经度
        if left_lon < 0:
            mid = left_lon + 180
        else:
            mid = left_lon - 180

        # 根据偏移量来调整原数据的经度
        new_lons = [lon - left_lon - 180 if lon >= left_lon else lon - left_lon + 180 for lon in lons]

        # 开始作图
        fig = plt.figure(dpi=300)
        ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree(central_longitude=mid))

        max_lat, min_lat = max(lats), min(lats)
        max_lon, min_lon = max(new_lons), min(new_lons)

        if max_lat - min_lat < 5 and max_lon - min_lon < 5:

            # 根据经纬度限制图片,不指定的话会按照作图的位置自动切割
            ax.set_extent([min_lon, max_lon, min_lat, max_lat], crs=ccrs.PlateCarree())

        # 地图中的各种元素
        ax.add_feature(cfeature.LAND)
        ax.add_feature(cfeature.OCEAN)
        ax.add_feature(cfeature.COASTLINE)
        ax.add_feature(cfeature.BORDERS, linestyle=':')
        ax.add_feature(cfeature.LAKES, alpha=0.5)
        ax.add_feature(cfeature.RIVERS)

        ax.plot(new_lons, lats, color="yellow")

        l_lon, r_lon = min(lons), max(lons)
        b_lat, t_lat = min(lats), max(lats)

        # 经纬度的刻度
        # 刻度一次变化多少
        # 5种粒度,30,10,5,1,0.1
        def get_unit(gap):
            if gap > 60:
                unit = 30
            elif gap > 20:
                unit = 10
            elif gap > 10:
                unit = 5
            elif gap > 2:
                unit = 1
            else:
                unit = 0.1
            return unit

        unit_la = get_unit(max_lat - min_lat)
        unit_lo = get_unit(max_lon - min_lon)

        # 两种写法
        # 第一种用python本身的写法
        # scale = 1000
        # x_label = [x / scale for x in
        #            range(int((l_lon * scale // (unit_lo * scale)) * unit_lo * scale), int(r_lon + unit_la) * scale,
        #                  int(unit_lo * scale))]
        # y_label = [x / scale for x in
        #            range(int((b_lat * scale // (unit_la * scale)) * unit_la * scale), int(t_lat + unit_la) * scale,
        #                  int(unit_la * scale))]

        # 第二种是借助numpy的arange
        x_label = list(np.arange(l_lon // unit_lo * unit_lo, r_lon + unit_lo, unit_lo))
        y_label = list(np.arange(b_lat // unit_la * unit_la, t_lat + unit_la, unit_la))

        n = len(docker_result)
        # 设置刻度
        ax.set_xticks(x_label, crs=ccrs.PlateCarree())
        ax.set_yticks(y_label, crs=ccrs.PlateCarree())
        # 设置刻度格式
        ax.xaxis.set_major_formatter(LongitudeFormatter())
        ax.yaxis.set_major_formatter(LatitudeFormatter())
        # width是刻度的大小,labelsize是刻度的字体大小
        ax.tick_params(axis='both', width=1, colors='black', labelsize="small")

        # 绘制港口
        for i in range(n):
            ax.scatter(port_lons[i] - left_lon - 180 if port_lons[i] >= left_lon else port_lons[i] - left_lon + 180,
                       port_lats[i], marker="*", s=200, label=port_names[i])

        # # 标注港口,太多了,不标注了
        # ax.legend(loc="best", prop={'size': 8})

        # # 横轴,纵轴
        # plt.xlabel("Lon")
        # plt.ylabel("Lat")

        plt.tight_layout()
        # 根据毫秒级时间戳构造文件路径
        ts = int(1000 * time.time())
        file_path = self.file_path_prefix + str(ts) + ".png"
        plt.savefig(file_path)

        return file_path




